using System;
using System.Collections.Generic;
using Engine;
using Engine.OldSerialization;

namespace RuthlessConquest {
    public class Game : ISerializable {
        public DynamicArray<Module> publicModules = new();

        public HashSet<Entity> publicEntities = new();

        public ReadOnlyList<Module> Modules => new(publicModules);

        public HashSet<Entity> Entities => publicEntities;

        public StepModule StepModule { get; }

        public PlayersModule PlayersModule { get; }

        public RouteFinderModule RouteFinderModule { get; }

        public BodiesModule BodiesModule { get; }

        public PlanetsModule PlanetsModule { get; }

        public ShipsModule ShipsModule { get; }

        public SpecialEventsModule SpecialEventsModule { get; }

        public ParticlesModule ParticlesModule { get; }

        public CameraModule CameraModule { get; }

        public MusicModule MusicModule { get; }

        public GameScreen Screen { get; set; }

        public GameCreationParameters CreationParameters { get; set; }

        public static Point2 WorldSize { get; } = new(16000, 9000);

        public event Action<Entity> EntityAdded;

        public event Action<Entity> EntityRemoved;

        public Game() {
            publicModules.Add(StepModule = new StepModule(this));
            publicModules.Add(PlayersModule = new PlayersModule(this));
            publicModules.Add(BodiesModule = new BodiesModule(this));
            publicModules.Add(PlanetsModule = new PlanetsModule(this));
            publicModules.Add(ShipsModule = new ShipsModule(this));
            publicModules.Add(RouteFinderModule = new RouteFinderModule(this));
            publicModules.Add(SpecialEventsModule = new SpecialEventsModule(this));
            publicModules.Add(ParticlesModule = new ParticlesModule(this));
            publicModules.Add(CameraModule = new CameraModule(this));
            publicModules.Add(MusicModule = new MusicModule(this));
        }

        public Game(GameCreationParameters creationParameters) : this() {
            CreationParameters = creationParameters;
            PlanetsModule.CreatePlanets();
        }

        public void Serialize(InputArchive archive) {
            CreationParameters = archive.Serialize<GameCreationParameters>("CreationParameters");
            foreach (Module module in Modules) {
                archive.Serialize(module.GetType().Name, module.GetType(), module);
            }
            List<Entity> list = new();
            archive.SerializeCollection("Entities", list);
            foreach (Entity item in list) {
                AddEntity(item);
            }
        }

        public void Serialize(OutputArchive archive) {
            archive.Serialize("CreationParameters", CreationParameters);
            foreach (Module module in Modules) {
                archive.Serialize(module.GetType().Name, module.GetType(), module);
            }
            archive.SerializeCollection("Entities", "Entity", Entities);
        }

        public void AddEntity(Entity entity) {
            if (entity.Game != null) {
                throw new InvalidOperationException();
            }
            publicEntities.Add(entity);
            entity.Game = this;
            EntityAdded?.Invoke(entity);
            entity.OnAdded();
        }

        /*public void AddEntity(int index, Entity entity) {
            if (entity.Game != null) {
                throw new InvalidOperationException();
            }
            publicEntities.Insert(index, entity);
            entity.Game = this;
            EntityAdded?.Invoke(entity);
            entity.OnAdded();
        }*/

        public void RemoveEntity(Entity entity) {
            if (entity.Game != this) {
                throw new InvalidOperationException();
            }
            EntityRemoved?.Invoke(entity);
            entity.OnRemoved();
            publicEntities.Remove(entity);
            entity.Game = null;
        }

        public void Update() {
            CameraModule.Update();
            MusicModule.Update();
            PlayersModule.Update();
            StepModule.Update();
        }

        public void Draw(Color colorTransform) {
            PlayersModule.Draw(colorTransform);
            ParticlesModule.Draw(colorTransform, 0);
            ShipsModule.Draw(colorTransform);
            PlanetsModule.Draw(colorTransform);
            ParticlesModule.Draw(colorTransform, 1);
        }
    }
}